package TCPMain;

import android.content.Context;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * TCP实现类
 * 功能：维护连接，发送数据，接收数据，返回处理结果
 */
public class Client {
    /**
     * 基础信息
     */
    private static final String TAG = "Client";
    private static int SERVER_PORT;//服务器端口号
    private static String SERVER_IP;//服务器IP地址
    private static String username;//用户名
    private static String password;//密码

    /**
     * json数据格式
     * "Command": "SendFolder",命令 SendFolder/SendFile
     * "SendFolder": "",客户端请求的文件夹地址
     * "SendFileNames": "",获取客户端请求的文件名列表
     * "SendFileLengths": "",获取客户端请求的文件长度列表
     * "GetFolder": "",获取客户端上传的文件夹地址
     * "GetFileNames": "",获取客户端上传的文件名列表
     * "GetFileLengths": "",获取客户端上传的文件长度列表
     */
    private JSONObject data;//数据，用于和服务器进行通讯的数据
    private String Command;//命令
    private String SendFolder;//客户端请求的文件夹地址
    private String SendFileNames;//获取客户端请求的文件名列表
    private String SendFileLengths;//获取客户端请求的文件长度列表
    private String GetFolder;//获取客户端上传的文件夹地址
    private String GetFileNames;//获取客户端上传的文件名列表
    private String GetFileLengths;//获取客户端上传的文件长度列表
    public FileProgress progress;
    /**
     * TCP相关信息
     */
    private Socket socket;//socket连接
    private DataOutputStream outputStream;//输出流
    private DataInputStream inputStream;//输入流
    File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

    public Client() {
        Client.SERVER_IP = AllData.getSERVER_IP();
        Client.SERVER_PORT = AllData.getSERVER_PORT();
        Client.username = AllData.getUsername();
        Client.password = AllData.getPassword();
        progress= new FileProgress();
        data = new JSONObject();
        //初始化数据
        try {
            data.put("username", username);
            data.put("password", password);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        // 初始连接使用超时控制
        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress(SERVER_IP, SERVER_PORT), 3000);
            socket.setSoTimeout(3000);
            outputStream = new DataOutputStream(socket.getOutputStream());
            inputStream = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            Log.e(TAG, "初始连接失败", e);
            close(); // 确保清理资源
        }
    }
    public Client(String ip,  int port){
        Client.SERVER_IP = ip;
        Client.SERVER_PORT = port;
        Client.username = AllData.getUsername();
        Client.password = AllData.getPassword();
        progress= new FileProgress();
        data = new JSONObject();
        //初始化数据
        try {
            data.put("username", username);
            data.put("password", password);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        //初始化socket连接
        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress(ip, port), 3000); // 连接超时
            socket.setSoTimeout(3000); // 读写超时
            outputStream = new DataOutputStream(socket.getOutputStream());
            inputStream = new DataInputStream(socket.getInputStream());
        } catch (Exception e) {
            Log.e(TAG, "连接失败", e);
            close(); // 确保关闭无效连接
        }
    }

    public synchronized JSONObject SendFolder(JSONObject data) {
        Log.d(TAG, "SendFolder:" + data.toString());
        int maxRetries = 3; // 最大重试次数
        int currentRetry = 0;
        long timeoutMillis = 3000; // 超时时间 3 秒

        while (currentRetry < maxRetries) {
            try {
                // 每次重试前检查连接
                if (socket == null || socket.isClosed() || !socket.isConnected()) {
                    reconnect(); // 使用已有重连方法
                }

                // 设置写操作超时
                socket.setSoTimeout((int) timeoutMillis);

                // 发送数据
                outputStream.writeUTF(data.toString());
                outputStream.flush();
                Log.d(TAG, "SendFolder发送数据完成");

                // 接收响应
                String serverResponse = inputStream.readUTF();
                Log.d(TAG, "服务器响应：" + serverResponse);
                return new JSONObject(serverResponse);

            } catch (IOException | JSONException e) {
                currentRetry++;
                Log.w(TAG, "第" + currentRetry + "次尝试失败: " + e.getClass().getSimpleName());

                // 最后一次重试仍失败时抛出异常
                if (currentRetry >= maxRetries) {
                    Log.e(TAG, "超过最大重试次数", e);
                    break;
                }

                // 指数退避策略（可选）
                try {
                    Thread.sleep(1000 * (long) Math.pow(2, currentRetry));
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        this.close();
        return data; // 返回原始数据或错误数据
    }


    /**
     * 下载服务器的文件
     *
     * @param data：json数据格式，包含命令，文件名，文件长度
     * @param fileProgress：进度条对象，用于在多线程中获取进度. &#064;return：返回是否下载成功
     */
    public boolean SendFile(JSONObject data, FileProgress fileProgress) throws JSONException {
        try {
            //发送命令
            this.data = data;
            outputStream.writeUTF(data.toString());
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        SendFileNames=this.data.getJSONArray("SendFileNames").getString(0);
        Log.d(TAG, "传入Client的数据：:" + data);
        // 接收文件内容
        //String savePath = dir + File.separator + data.getString("SendFileNames");
        //创建文件
        JSONArray fileNames = data.getJSONArray("SendFileNames");
        String name = "";
        for (int i = 0; i < fileNames.length(); i++) {
            name = fileNames.getString(i);
        }
        String filename=getFileName(name);
        Log.d(TAG, "创建文件名：" + filename);
        File file = new File(dir, String.valueOf(filename));
        if (!file.exists()) {//文件不存在，创建文件
            try {
                if (file.createNewFile()) {//创建文件成功
                    writeFile(file, true);
                } else {
                    Log.d(TAG, file.createNewFile() + ":文件创建失败");
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "文件创建失败");
            }
        } else {//文件存在，覆写文件
            writeFile(file, false);
        }

        Log.d(TAG, "文件下载完成，保存路径：" + file);
        return true;
    }

    private boolean writeFile(File file, boolean isAppend) {
        Log.d(TAG, "开始写入文件"+file.getPath());
        long expectedFileLength = 0L;
        // 获取文件的预期长度
        try {
            JSONArray fileLengths = data.getJSONArray("SendFileLengths");
            if (fileLengths.length() > 0) {
                expectedFileLength = fileLengths.getLong(0);
            } else {
                Log.e(TAG, "SendFileLengths 数组为空");
                return false;
            }
        } catch (JSONException e) {
            Log.e(TAG, "解析 SendFileLengths 失败", e);
            return false;
        }
        try (FileOutputStream fos = new FileOutputStream(file, isAppend)) {
            byte[] buffer = new byte[102400];
            int bytesRead;
            long useBytesRead = 0;
            long totalBytesRead = 0;
            while (totalBytesRead < expectedFileLength) {
                if (Thread.currentThread().isInterrupted()){
                    Log.d(TAG, "下载线程中断");
                }
                bytesRead = inputStream.read(buffer);
                if (bytesRead == -1) {
                    Log.e(TAG, "提前结束读取，读取到的字节数: " + totalBytesRead + ", 预期字节数: " + expectedFileLength);
                    Log.e(TAG, "连接中断，尝试重连...");
                    reconnect();
                    //retryCount++;
                    continue;
                }
                // 更新进度（每写入1%或至少每100KB更新一次）
                if (progress != null && (totalBytesRead % 102400 == 0 ||
                        (totalBytesRead * 100 / expectedFileLength) > progress.getProgress())) {
                    progress.update(
                            file.getName(),
                            totalBytesRead,
                            expectedFileLength
                    );
                }
                fos.write(buffer, 0, bytesRead);
                fos.flush();
                totalBytesRead += bytesRead;
//                Log.d(TAG, "累计写入文件长度：" + totalBytesRead);
            }
            if(totalBytesRead == expectedFileLength){
                fos.flush();
                Log.d(TAG, "文件0完成");
                socket.close();
            }else{
                Log.e(TAG, "文件写入失败，实际写入长度：" + totalBytesRead + ", 预期字节数: " + expectedFileLength);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    // 新增重连方法
    private synchronized boolean reconnect() {
        try {
            close();
            socket = new Socket();
            socket.connect(new InetSocketAddress(SERVER_IP, SERVER_PORT), 3000); // 连接超时
            socket.setSoTimeout(5000); // 读写超时
            outputStream = new DataOutputStream(socket.getOutputStream());
            inputStream = new DataInputStream(socket.getInputStream());
            Log.i(TAG, "重连成功");
            return true;
        } catch (IOException e) {
            Log.e(TAG, "重连失败", e);
            return false;
        }
    }

    public void close() {
        try {
            if (socket != null) {
                socket.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean isClose() {
        return socket.isClosed();
    }
    // 获取文件名（包括后缀）的辅助方法
    public static String getFileName(String filePath) {
        // 找到最后一个反斜杠的位置
        int lastBackslashIndex = filePath.lastIndexOf("\\");
        if (lastBackslashIndex != -1) {
            return filePath.substring(lastBackslashIndex + 1);
        } else {
            return filePath; // 如果路径中没有反斜杠，直接返回整个字符串
        }
    }
}
